C语言笔试 最终版
题型分布
一、选择题(共20小题,每小题1分,共20分)
二、填空题(共10空,每空1分,共10分)
三、判断题(共10小题,每小题1分,共10分)
四、程序阅读题(共5小题,每小题4分,共20分)
五、算法设计题(共2小题,每小题8分,共16分)
六、程序设计题(共2小题,每小题12分,共24分)
一、选择题(共20小题,每小题1分,共20分)
1、考察基础知识
题目:下列叙述中正确的是______。
A、C程序的基本组成单位是语句
B、C语句必须以分号结束
C、C程序中的每一行只能写一条语句
D、C程序必须在一行内写完
- 解析:
- C语言中,程序基本组成单位是 函数 **,**基本执行单位是 语句 ,以分号结尾。
题目:可以将用高级语言编写的源程序转换为目标程序的程序是______。
A. 汇编程序 B. 解释程序 C. 编辑程序 D. 编译程序
- 解析:
- c语言是编译型语言,python是解释型语言。
2、考察标识符
题目:下列选项中,不能用作c语言标识符的是_____
A、print B、FOR C、&a D、_00
- 解析:
- 只能是字母数字下划线,"&"不合法。
3、考察格式化输出与 ASCII 码
题目:已知字符 a 的 ASCII 码为 97,执行语句 printf("%c%d", 'b', 'b'+1);后输出结果是________
A、b99 B、98c C、9899 D、bc
- 解析:
'b'的 ASCII 码是 98(因为'a'是 97,'b'紧随其后)。- 第一个格式符
%c对应参数'b':输出字符形式 b。 - 第二个格式符
%d对应参数'b'+1:即98 + 1 = 99。%d要求输出整数形式,所以输出 99。 - 拼接起来就是
b99。
**题目:**已知字符 a 的 ASCII 码为 97,执行语句 printf("%d,%c", 'b', 'b'+1);后输出结果是________
A、98,b B、语句不合法 C、98,99 D、98,c
4、考察字符串常量
**题目:**表达式sizeof("key")的值是________
A、1 B、2 C、3 D、4
- 解析:
- 0 1 2 3
- k e y \0
5、考察数学库
**题目:**要调用数学函数时,在#include命令行应该包含 ________
A、stdio.h B、string.h C、math.h D、ctype.h
6、考察运算符
**题目:**若变量已正确定义,则语句if(a>b) k=0; else k=1和_______ 等价。
A、k=(a>b)?1:0;
B、k=a>b;
C、k=a≤b;
D、a≤b?0:1;
- 解析:
- 列出一个逻辑表
- 情况 1:当
a > b成立(真)时 \(\rightarrow\)k被赋值为 0。 - 情况 2:当
a > b不成立(假)时 \(\rightarrow\)k被赋值为 1。
- 情况 1:当
- 逐个分析选项
- 选项 A:
k=(a>b)?1:0;- 含义:这是三目运算符。如果
a>b为真,取冒号前面的值(1);否则取冒号后面的值(0)。 - 结果:真 \(\rightarrow\) 1,假 \(\rightarrow\) 0。
- 结论:这与原题目的逻辑刚好相反。
- 含义:这是三目运算符。如果
- 选项 B:
k=a>b;- 含义:直接将关系表达式的结果赋值给 k。
- C语言规则:关系运算(如
>)的结果只有两种:真为 1,假为 0。 - 结果:
- 如果
a > b为真,表达式值为 1 \(\rightarrow\)k = 1。 - 如果
a > b为假,表达式值为 0 \(\rightarrow\)k = 0。
- 如果
- 结论:这也与原题目逻辑相反(同 A 选项)。
- 选项 C:
k=a<=b;- 含义:判断
a是否小于等于b,并将结果(1或0)赋值给k。 - 逻辑转换:
- 当原题条件
a > b成立(真)时 \(\rightarrow\) 它的反面a <= b就不成立(假,值为 0)。此时k = 0。(符合原题) - 当原题条件
a > b不成立(假)时 \(\rightarrow\) 它的反面a <= b就成立(真,值为 1)。此时k = 1。(符合原题)
- 当原题条件
- 结论:完全等价。
- 含义:判断
- 选项 D:
a<=b?0:1;- 含义:这是一个合法的 C 语言表达式,计算出了结果。
- 致命伤:没有赋值动作! 也就是没有写
k = ...。算出结果后,没有把值给变量k,所以这条语句执行完,k的值不会发生改变。 - 结论:不等价。
- 选项 A:
- 列出一个逻辑表
**题目:**表达式 x&&1 等价于______。
A. x0 B. x1 C. x!=0 D. x!=1
- 解析:
- 运算符:
&&是逻辑与运算符。 - 规则:“同真才真,一假则假”。只有当两个操作数都为真(非0)时,结果才为 1;否则为 0。
- 操作数分析:
- 右边的操作数是
1:这是真值(True)。 - 左边的操作数是
x:这是变量。
- 右边的操作数是
- 因为右边已经是真了,所以整个表达式的结果完全取决于
x: - 如果
x是真(非0),则真 && 真\(\rightarrow\) 结果为 1 (真)。 - 如果
x是假(0),则假 && 真\(\rightarrow\) 结果为 0 (假)。 - 结论: 这个表达式的作用就是判断 “x 是不是真的”(即 x 是不是非 0)。
- 运算符:
7、考察自增自减
**题目:**已知有声明int x,a=3,b=2;,则执行赋值语句 x=a>b++?a++:b++后,变量x、a、b的值分别为_____
。
A、3 4 3
B、3 3 4
C、3 3 3
D、4 3 4
- 解析:
-
x=a>b++?a++:b++ -
step1: a>b? → 3>2 → true
b++ → b=2+1=3 -
step2: x=a++ → x=a=3
a++ → a=3+1=4 -
step3: x=3 a=4 b=3
-
题目:
int a=5;
int b,c;
b=++a;
c=a++;
a, b, c的值最终是多少?
++a a=6
b=a=6
c=a=6
a++ a=7
a=7 b=6 c=6
题目:
int a=0,b=3,c=2;
if(a++&&b++)
c--;
else if(a+1==6 || b--)
c++;
a、b、c的值最终是多少?
&& 且 有0则0 左边为0 直接不看右边
if(a++&&b++) -> if(0&&b++) -> if(0)
c--; 不执行 不执行
a用完后++ a=0+1=1
|| 且 有1则1 左为1 不看右边
else if(a+1==6 || b--) 1+1==6 × (0 || 3) -> true (1)
b用完后-- b=3-1=2
执行里面 c++ c=2+1=3
a=1 b=2 c=3
8、考察 if 语句的作用范围与逗号表达式
题目:定义 int x=4,y=3,z=5;,下列四个程序段中有 3 个程序段的执行结果相同,另一个执行结果不同的是________
A、if(x>y) z=x,x=y,y=z;
B、if(x>y) { z=x,x=y,y=z;}
C、if(x>y) z=x;x=y;y=z;
D、if(x>y) {z=x;x=y;y=z;}
- 解析:
x > y即4 > 3,条件为真。- A:
if(x>y) z=x,x=y,y=z;—— 逗号表达式z=x,x=y,y=z被视为一条语句。因为条件为真,这三个赋值都会执行。 - B:
if(x>y) { z=x,x=y,y=z;}—— 花括号内包含一条逗号表达式语句,效果同 A,全部执行。 - D:
if(x>y) {z=x;x=y;y=z;}—— 花括号内包含三条独立语句,因为有花括号包裹,全部受if控制,全部执行。 - C:
if(x>y) z=x; x=y; y=z;—— 陷阱!if只能控制紧跟其后的第一条语句(即z=x;)。后面的x=y;和y=z;与if无关,无论条件真假都会执行。 - 虽然在本题具体数值下,条件为真,看起来大家都执行了。但从程序逻辑结构来看,C 的控制范围与其他三项不同(如果
x>y为假,A/B/D 什么都不做,而 C 依然会执行后两句赋值)。
9、考察do-while 循环
题目:程序段 int n=3; do{ printf("%d",n--);}while(!n); 的执行结果是________
A、3 2 1 B、死循环 C、2 D、3
- 解析:
- 第一次执行循环体:打印
n(3),然后n自减变为 2。 - 判断条件:
while(!n)即while(!2)。在 C 语言中,2 是真,!2就是假 (0)。 - 结果:条件为假,循环终止。
- 所以屏幕上只输出了一个 3。
- 第一次执行循环体:打印
10、考察无符号数
题目:设 int i,a=1; unsigned j;,则下列语句执行会出现无限循环的语句是________
A、for(j=15;j>0;j-=2) a++;
B、for(j=0;j>15;j+=2) a++;
C、for(i=0;i>15;i+=2) a++;
D、for(i=15;i>0;i-=2) a++;
- 解析:
- A:
for(j=15; j>0; j-=2)。注意j是 unsigned (无符号整数)。- 序列变化:15, 13, ..., 3, 1。
- 下一步:
1 - 2。在数学上是 -1,但在计算机无符号数运算中,它会变成一个巨大的正整数(通常是2^32 - 1)。 - 结果:这个巨大的数依然
> 0,循环条件永远成立,导致死循环。
- B/C: 初始条件
0 > 15不成立,一次都不执行。 - D:
i是int(有符号)。1 - 2变成-1。-1 > 0为假,循环正常结束。
- A:
11、考察循环语句的特性
题目:下列关于循环语句 for、while、do-while 的叙述中正确的是________
A、三种循环语句都可能出现无穷循环
B、三种循环语句中都可以缺省循环终止条件表达式
C、三种循环语句的循环体都至少被无条件地执行一次
D、三种循环语句的循环体都必须放入一对花括号中
- 解析:
- A: 正确。只要条件设置不当(如
while(1)),三种循环都可能死循环。 - B: 错误。
for(;;)可以缺省条件,但while()和do-while()的括号内必须有表达式(哪怕是常数 1)。 - C: 错误。
for和while是“先判断后执行”,可能一次都不执行。只有do-while至少执行一次。 - D: 错误。如果循环体只有一条语句,花括号
{}可以省略。
- A: 正确。只要条件设置不当(如
**题目:**在c语言中,whiile和do while循环的主要区别是______
A、do while 的循环体至少无条件执行一次
B、do while 允许从外部转到循环体内
C、while的循环控制条件比do while的更严格
D、do while 的循环体不能是复合语句
12、考察数组声明
题目:下列数组声明语句正确的是________
A、int n, a[n];
B、int a[];
C、int a[2][3]={{2},{1},{3}};
D、int a[][3]={{2},{1},{3}};
- 解析:
- A:
n是变量且还没有被初始化,不能用来定义数组长度。 - B:只有在两种情况下可以省略数组长度:
- 声明的同时进行了初始化:
int a[] = {1, 2, 3};(编译器会自动数个数)。 - 作为函数的形参:
void func(int a[])。
- 声明的同时进行了初始化:
- C:越界(初始值过多)。
- 分析: 声明
a[2][3]意味着这个数组只有 2 行。 - 初始值:
{{2}, {1}, {3}}这里的三个大括号代表试图填充 3 行 数据。
- 分析: 声明
- D:“行可省,列不可省”。
- 声明了列数是
3(不可省略,必须告诉编译器一行有多宽)。 - 省略了行数
[]。 - 编译器去数后面的初始值:
{2}是第一行,{1}是第二行,{3}是第三行。 - 编译器自动推导出:哦,原来有 3 行。
- 最终等价于:
int a[3][3] = {{2,0,0}, {1,0,0}, {3,0,0}};。
- 声明了列数是
- A:
13、考察函数形参声明
题目:下列关于函数形参的声明中正确的是________
A、int a[][]
B、int a[]
C、int a[]={0}
D、int a[2][]
- 解析:
- B:
int a[]—— 正确。作为函数参数时,一维数组可以不写长度(本质是退化为指针int *a)。 - A/D:
int a[][]和int a[2][]—— 错误。多维数组作为参数,第一维可以省略,但后面的维度必须写明(例如int a[][5]是对的)。编译器需要知道列宽来计算地址。 - C:
int a[]={0}—— 错误。函数形参只是声明,不能在参数列表中进行初始化赋值。
- B:
题目:下列函数定义的头部中正确的是______。
A. int fun(int a[], b) B. int fun(int a[], int a)
C. int fun(int a, int b) D. int fun( int char a[][], int b)
14、考察构造数据类型
题目:在c语言中,下列属于构造类型的是
A、整型 B、实型 C、指针类型 D、结构体类型
题目:若要使表达式 x++ 无语法错误,则变量 x 不能声明为______。
A. float x; B. long x; C. int *x; D. struct{int y;}x;
15、考察文件写入
题目:下列函数中,可以把整型数据以二进制形式写入文件中的函数是________
A、fputc() B、fwrite()
C、fread() D、fgetc()
- 解析:
- B:
fwrite()—— 专门用于二进制块读写的函数。 - A:
fputc()—— 写入单个字符(文本模式)。 - C/D:
fread()/fgetc()—— 都是读取函数,不是写入。
- B:
16、考察文件关闭
题目:当文件被正常关闭时,fclose()函数的返回值是________
A、-1
B、true
C、0
D、1
- 解析:
fclose关闭成功返回 0。- 关闭失败返回 EOF(通常定义为 -1)。
17、考察宏定义
题目:在宏定义语句 #define PI 3.1415926 中,宏名 PI 的是________
A、一个字符串
B、一个单精度数
C、一个常量
D、一个双精度数
- 解析:
- 在 C 语言术语中,用
#define定义的标识符被称为符号常量(Symbolic Constant)。 - 它不是字符串(没有双引号),也不是变量。虽然它代表一个双精度数,但“符号常量”是更准确的定义分类。
- 在 C 语言术语中,用
18、考察宏替换
题目:设有定义和声明如下:
#define d 2
int a = 3;
float b = 3.14;
char c = 'd';
以下表达式中有语法错误的是_______
A、x++
B、y++
C、c++
D、d++
- 解析:
- 宏定义是纯文本替换。预处理阶段会将代码中所有的
d替换为2。 - A:
x++->5++(变量自增,正确)。 - B:
y++->3.83++(变量自增,正确)。 - C:
c++->c是变量,可以自增。 - D:
d++-> 被替换为2++。- 错误原因:自增运算符
++只能用于变量(左值),不能用于常量或表达式。你不能强迫数字 2 变成 3。
- 错误原因:自增运算符
- 宏定义是纯文本替换。预处理阶段会将代码中所有的
注:上面的内容是原题,接下来的内容为推测必考点:
19、考察数组常识
在C语言中,数组下标是从( )开始的。
A. 0
B. 1
C. 2
D. 任意整数
若有定义 int arr[5];,则数组arr的最后一个元素是( )。
A. arr[0]
B. arr[4]
C. arr[5]
D. arr[6]
以下关于二维数组的描述,正确的是( )
A. 二维数组在内存中是按列存储的
B. 二维数组可以看作是一维数组的数组
C. 二维数组的行数和列数必须在编译时确定
D. 二维数组的所有元素必须具有相同的值
若定义 int a[10], b[3][3]; ,则以下对数组元素赋值的语句中,不会出现越界访问的是( )
A. a[-1]=0 B. a[10]=0 C. b[3][0]=0 D. b[0][3]=0
若定义 int k, a, b; unsigned long y = 5; double x = 1.23; ,则下列表达式中正确的是______。
A. x%(-3) B. x=y[5] b C. k=(a=1,b=2,&a+&b) D. a+=a-=(b=3)*(a=2)
若定义 int a[3][2]={1,2,3,4,5,6}; ,则数组元素______的值为6。
A. a[3][2] B. a[2][1] C. a[1][2] D. a[2][3]
20、考察函数常识
什么是形参,什么是实参?
- 形参 (Formal Parameter):
- 定义时括号里的变量。
- 它是“占位符”,函数未被调用时不占内存,只有调用时才分配内存。
- 例子:
void fun(int a)里的a。
- 实参 (Actual Parameter):
- 调用时括号里传递的具体数值或变量。
- 它是“实际的数据”,必须有确定的值。
- 例子:
fun(2)里的2。k
- 关系: 发生函数调用时,实参的值会单向复制给形参(值传递)。
以下函数调用语句中有______个实参。
func((a1,a2,a3),(a4,a5));
A.2 B. 5 C. 1 D. 不合法
21、考察格式化输出
执行printf("%x",100);后的输出结果是______。
A. 100 B. 0x100 C. 0X64 D. 64
下列语句中有语法错误的是______。
A. printf("%d",0e); B. printf("%f",0e2); C. printf("%d",0x2); D. printf("%s","0x2");
22、考察表达式运算结果
在C程序中,用______表示逻辑值“真”。
A. 小于0的数 B. 非0的数 C. 非1的数 D. 大于0的数
问题:int k = a > b;
k = ?
若a>b -> true -> 1
k=1
若a>b -> false -> 0
k=0
23、考察switch case
24、考察字符串比较函数strcmp()
如何通过strcmp函数比较两个字符串?
strcmp 函数定义在 <string.h> 头文件中,用于 比较两个字符串。
函数原型:
int strcmp(const char *str1, const char *str2);
返回值说明:
返回 0:两个字符串 完全相同
返回 < 0:str1 小于 str2(按 ASCII 码顺序比较)
返回 > 0:str1 大于 str2(按 ASCII 码顺序比较)
注意:strcmp 是区分大小写的。例如 "abc" 和 "ABC" 不相等。
二、填空题(共10空,每空1分,共10分)
1、考察三目运算符
题目:若定义 int a=5; 则表达式 (a>0 ? a : -a)的值为
5
考点:
- 语法:
表达式1 ? 表达式2 : 表达式3 - 解析:
- 先判断
表达式1(即a>0) 是否为真。 - 因为
a=5,所以5>0成立(为真)。 - 取
表达式2(即a) 的值作为整个表达式的结果。 - 如果是假,则取
表达式3(-a)。
- 注:这其实就是求绝对值的逻辑。
- 先判断
2、考察循环结构
**题目:**若循环结构的循环体至少要执行一次,则最适合描述该循环结构的语句是 do while 语句。
3、考察数组
**题目:**若有数组a,且数组元素a[0]~a[9]的值为9,4,12,8,2,10,7,5,1,3,则:
该数组中,可用的最小下标为 0 ,最大下标为 9
4、考察函数
当 return 语句被执行时,程序的执行流程无条件地从一个函数跳转到另一个函数。
用于定义外部函数的关键字是 extern ,用于定义内部函数 (静态函数)的关键字是 static 。
5、考察输入输出
题目:scanf()函数的输入项必须是 地址 ,不能是变量名。
考点:
- scanf 原理:
scanf需要修改变量的值,在 C 语言中,要修改一个变量,必须知道它在内存中的位置。 - 解析:
- 错误写法:
scanf("%d", a);(只传了值,scanf 找不到屋子在哪,没法把东西放进去)。 - 正确写法:
scanf("%d", &a);(&取地址符,告诉 scanf 变量a的门牌号)。 - 特例:如果是字符数组(字符串)
char str[10], 写scanf("%s", str)是对的,因为数组名本身就是地址。
- 错误写法:
6、考察字符输入输出
题目:getchar()和putchar()函数只能用于 单个 字符的输入和输出。
考点:
- 函数功能差异:
printf/scanf:格式化,可以处理各种类型,可以处理多个。puts/gets:专门处理字符串(一串字符)。putchar/getchar:专门处理单个字符 (char)。
- 解析:
getchar一次只读一个字,putchar一次只打一个字。
7、考察逻辑运算与算术运算
题目:若定义“int m=23,n=5;”,则表达式 !m+m%n 的值是
3
考点:
- 运算符优先级:
!(逻辑非) >%(取余) >+(加法)。 - 逻辑值的数值化:真为 1,假为 0。
- 解析:
- 先算
!m:m是 23 (非 0 即为真)。对“真”取反就是“假”。在 C 语言中,假的值是 0。所以!m变成了0。 - 再算
m%n:23 除以 5,商 4 余 3。所以m%n是3。 - 最后算加法:
0 + 3 = 3。
- 先算
**题目:**若定义“int x=33,y=4;”,则表达式 !x+x/y 的值是
8
解析:
!33 → 0
0 + x / y
33 / 4 → 8
0 + 8
0 + 8 = 8
8、考察结构体
题目:结构体的每个成员的数据类型可以是基本数据类型,也可以是 构造 类型。
考点:
- 数据类型分类:
- 基本类型:int, float, char 等。
- 构造类型:数组、结构体 (struct)、共用体 (union)。
- 解析:结构体里可以套娃。比如“学生”结构体里,可以有一个成员是“生日”,而“生日”本身又是一个包含年月日的小结构体。
9、考察sizeof
题目:使用sizeof()函数计算结构体List的长度的表达式是 sizeof(struct List)
考点:
- sizeof 用法:
sizeof是运算符,不是函数(虽然长得像)。 - 语法:
sizeof(类型名)或sizeof(变量名)。 - 解析:
- 题目说“结构体 List”,通常指的是
struct List { ... };。 - 在 C 语言中,引用这个类型时必须带上
struct关键字,即struct List。 - 所以计算大小时写
sizeof(struct List)。
- 题目说“结构体 List”,通常指的是
> 注:上面的内容是原题,接下来的内容为推测必考点:
10、考察基础知识
程序设计语言的基本功能包括数据表达与 数据处理 。
算法应具备如下几个特征:确定性、有穷性、可行性和 一个或多个输出 。
C语言中共有 32 个关键字。
一个完整的C程序可以有多个函数,其中有且只能有一个 main() 函数。C程序总是从 main ()函数 开始执行。
字符(char)是 1,短整(short)是 2。 整型(int)、单浮点(float) 都是 4。 双浮点(double) 是 8。
任何程序的结构都可以通过3种基本结构进行组合来实现,分别是 顺序结构、 选择结构 和循环结构。
11、考察输入输出
printf函数调用时,格式说明符数量与实际提供的参数数量不一致时会发生什么?
格式说明符少于参数
int a = 10, b = 20;
printf("%d", a, b);
%d 只有一个说明符,但提供了两个参数
结果:
只使用第一个参数 a
b 被忽略
不会报错,也不会打印 b
执行printf("%d;%d",a,b,c,d);后屏幕上将输出 2 个整数。
已知有声明和语句int a;scanf("a=%d",&a);,欲使从键盘上为变量a输入的值为2,则正确的输入应是 a=2 。
格式修饰符都有哪些?
%d, %3d, %u, %x, %#x, %ld, %f, %lf, %4.2f, %c, %s的含义分别是什么?
%d
含义:输出十进制有符号整数(int)
%3d
含义:输出宽度至少为 3 的十进制整数,右对齐
若数字不足 3 位,左边补空格
若超过 3 位,直接原样输出
%u
含义:输出无符号十进制整数(unsigned int)
同一块内存,按“无符号”方式解释
%x
含义:以十六进制输出整数(小写字母 a–f)
仅影响显示,不改变变量的值
%#x
含义:以十六进制输出,并自动加前缀 0x
字母仍为小写
%ld
含义:输出 long int 类型的十进制整数
用于 long 型整数
%f
含义:输出浮点数(float / double)
默认 保留 6 位小数
%lf
含义:
printf 中:等价于 %f
scanf 中:表示读入 double
%4.2f
含义:
总宽度至少为 4
小数点后保留 2 位
默认右对齐
%c
含义:输出一个字符
对应 char 类型或字符常量
%s
含义:输出字符串(以 \0 结尾的字符数组)
不输出 \0
printf("%c,%d", 'A', 'A');
%c
'A' 作为字符输出
输出字符:A
%d
'A' 实际是字符常量
在 C 语言中,字符本质是整数(ASCII 码)
'A' 的 ASCII 值是 65
输入输出的格式控制字符中,int型数据采用 %d ,float型数据采用 %f ,char型数据采用 %c。
printf(“%c,%d”, ‘A’,’A’);
实际输出结果:
A,65
12、考察字符数组
以下代码段的输出结果是 China 。
char ch[20] = {"China\0Beijing"};
printf("%s", ch);
sizeof(“hello”)=?
6
末尾有\0
13、考察指针(应该不考 看不懂可跳过)
第 1 题:指针与变量别名
int k=1,j=2;
int *p=&k, *q=&j; // p 指向 k,q 指向 j
(*p)++; // 1. 取 p 指向的值(k)并自增。k 从 1 变成 2。
k=j; // 2. 把 j 的值(2)赋给 k。此时 k 还是 2。
(*p)++; // 3. 取 p 指向的值(k)并自增。k 从 2 变成 3。
printf("%d, %d",k,j);
结果: 3, 2
C
int k=1,j=2;
int *p=&k, *q=&j; // p 指向 k,q 指向 j
(*p)++; // 1. 取 p 指向的值(k)并自增。k 从 1 变成 2。
k=j; // 2. 把 j 的值(2)赋给 k。此时 k 还是 2。
(*p)++; // 3. 取 p 指向的值(k)并自增。k 从 2 变成 3。
printf("%d, %d",k,j);
核心考点:
*p就是k的替身。动*p就是动k。q和j在这道题里其实是打酱油的,j的值全程没变,一直是 2。
第 2 题:循环陷阱与指针移动
结果: 0BC
代码拆解: 第一部分:分号陷阱
int k; char *s="ABC";
for(k=10; k!=0; k--); // ⚠️ 注意这里有个分号!
printf("%d",k);
C
int k; char *s="ABC";
for(k=10; k!=0; k--); // ⚠️ 注意这里有个分号!
printf("%d",k);
- 这个
for循环后面直接跟了分号,说明循环体是空的。 - 它会一直空转,直到
k减到0为止。 - 所以
printf输出的是 0。
第二部分:指针的“吃字”现象
while(*s++) putchar(*s);
C
while(*s++) putchar(*s);
这是一个非常经典的逻辑陷阱:
- 判断阶段:先取
*s的值(也就是 'A')来判断循环条件(真)。 - 自增阶段:判断完瞬间,
s++生效,指针向后移一位,指向了 'B'。 - 执行阶段:执行
putchar(*s)。注意!此时s已经指向 'B' 了。- 所以第一次输出的是 B('A' 被跳过了)。
- 下一次循环:
- 判断:取
*s('B') 为真。 - 自增:
s移向 'C'。 - 执行:输出 C。
- 判断:取
- 再下一次:
- 判断:取
*s('C') 为真。 - 自增:
s移向 '\0' (结束符)。 - 执行:输出结束符(看不见)。
- 判断:取
- 最后:
- 判断:取
*s('\0') 为假,循环结束。
- 判断:取
所以最终连起来是 0 B C。
第 3 题:全局变量、传值与传址
结果: 4,3,4,2,7--1,5,4,1,6
这道题必须分步骤跟踪内存。 全局变量初始状态: x=5, y=2, z=0
第一步:p(&x, y) 调用
- 传参:
- 形参
*x指向全局x(5)。(传址) - 形参
y是全局y的副本,值为 2。(传值)
- 形参
- 函数内执行:
--*x;-> 全局x减 1,变为 4。y++;-> 局部副本y加 1,变为 3 (不影响全局 y)。z=*x+y;-> 全局z= 全局x(4) + 局部y(3) = 7。printf: 输出*x(4) 和y(3)。
- 函数内输出:
4, 3,
第二步:main 函数中间的 printf
- 此时回到主函数,看全局变量:
x刚才被改成了 4。y刚才没被改(传的是值),还是 2。z刚才被改成了 7。
- 主函数输出:
4, 2, 7--
第三步:p(&y, x) 调用
- 传参:
- 形参
*x指向全局y(2)。(注意!这次指针指着 y) - 形参
y是全局x的副本,值为 4。(传值)
- 形参
- 函数内执行:
--*x;-> 指针指向的是全局y,所以全局y减 1,变为 1。y++;-> 局部副本y(也就是那个4) 加 1,变为 5。z=*x+y;-> 全局z= 全局y(1) + 局部y(5) = 6。printf: 输出*x(1) 和y(5)。
- 函数内输出:
1, 5,
第四步:main 函数最后的 printf
- 看全局变量最终状态:
x没变,还是 4。y被改成 1 了。z被改成 6 了。
- 主函数输出:
4, 1, 6
三、判断题(共10小题,每小题1分,共10分)
注:这部分没明确暗示,把一些没归类好的但90%考的整理进来:
long long 占8字节 使用%lld格式化输出
unsigned long long 使用%llu格式化输出
switch语句中,各个case和default的出现次序影响执行结果吗? 不影响
函数调用之前是否需要先声明?
- 原则上需要。 编译器必须先认识这个函数(知道它的名字、参数类型、返回值类型),才能检查调用是否合法。
- 特例: 如果函数的定义(完整代码)写在调用它的位置之前,那么定义本身就充当了声明,不需要额外写声明语句。
是否可以调用 main 函数?
- 在 C 语言中:可以。
main函数本质上也是一个函数,可以在程序中递归调用自己(虽然在实际开发中极少这样做,容易死循环)。 - (注:在 C++ 中是禁止调用 main 函数的,但 C 语言考试通常认为可以)。
c语言本身提供输入输出语句吗? C 语言的“语言本身”不提供输入输出语句;输入输出是由标准库提供的。
-
C 语言核心语法(如变量、表达式、控制结构、函数定义等)中没有内建的 I/O 功能。
也就是说,像
printf、scanf这样的函数不是语言内置语句。 -
I/O 由 C 标准库提供
标准库
<stdio.h>中定义了常用的输入输出函数,例如:printf():格式化输出到标准输出scanf():从标准输入读取格式化数据putchar()、getchar():字符输入输出fopen()、fread()、fwrite()等:文件操作
-
所以:C 语言的 I/O 是“库函数”,而不是“语言关键字或语句”。
这与一些语言(如 Python 的
print是关键字或内建函数)不同。
C语言为文件的操作提供了一系列标准函数,它们定义在stdio.h中,因此进行文件操作时需要使用文件包含命令 #include<stdio.h> 。文件操作涉及下列一些函数。
(1) 字符读写函数:fputc() 和 fgetc() 。
(2) 字符串读写函数: fputs() 和 fgets() 。
(3) 格式化读写函数: fscanf() 和 fprintf() 。
(4) 二进制读写函数: fread() 和 fwrite() 。
四、程序阅读题(共5小题,每小题4分,共20分)
1、考察字符型变量(ASCII码 大小写转换)
非真题:(但由书上两道可能是真题的题目合成)
#include <stdio.h>
int main()
{
char c1, c2;
// 步骤 1:初始化
c1 = 65; // 考点 A:直接赋整数值
c2 = 'b'; // 考点 A:赋字符形式
// 步骤 2:运算(大小写转换)
c1 = c1 + 32; // 考点 B:大写转小写
c2 = c2 - 32; // 考点 B:小写转大写
// 步骤 3:输出
printf("%c, %d\n", c1, c1);
printf("%c, %d\n", c2, c2);
return 0;
}
初始化的本质(考点 A)
c1 = 65;- 内存视角: 计算机在
c1里存入二进制的 65。 - 理解: 虽然赋值的是整数,但在 ASCII 码表中,65 对应的就是大写字母 'A'。
- 内存视角: 计算机在
c2 = 'b';- 内存视角: 计算机查表找到 'b' 的 ASCII 码是 98,存入二进制的 98。
运算逻辑(考点 B)
c1 = c1 + 32;- 即
65 + 32 = 97。 - 在 ASCII 码性质中,大写字母 + 32 = 对应的小写字母。
- 此时
c1变成了 97(即字符 'a')。
- 即
c2 = c2 - 32;- 即
98 - 32 = 66。 - 在 ASCII 码性质中,小写字母 - 32 = 对应的大写字母。
- 此时
c2变成了 66(即字符 'B')。
- 即
输出格式
- 第一行
printf("%c, %d", c1, c1):%c输出c1(97) 对应的字符 \(\rightarrow\) a%d输出c1(97) 对应的数值 \(\rightarrow\) 97
- 第二行
printf("%c, %d", c2, c2):%c输出c2(66) 对应的字符 \(\rightarrow\) B%d输出c2(66) 对应的数值 \(\rightarrow\) 66
答案:
a, 97
B, 66
2、考察 Sizeof 与 运算符优先级
非真题:(但由书上两道可能是真题的题目合成)(可能不考这个 考13456五道)
#include <stdio.h>
int main()
{
int a, b;
// 考点 A:混合运算的类型提升(括号内)
// 3(int) + 5.0(double) -> 结果类型为 double
a = sizeof(3 + 5.0);
// 考点 B:优先级与隐式转换(无括号)
// sizeof 3 先算,结果再加 5.0,最后赋值给 int b
b = sizeof 3 + 5.0;
// 考点 C:基本数据类型与字符串(\0)
// sizeof(short) vs sizeof("China")
printf("%d, %d, %d, %d\n", sizeof(short), a, b, sizeof("China"));
return 0;
}
- 基本类型的长度
sizeof(short)
- 分析:这是最基础的死记硬背考点。
- 规则:
short(短整型)在标准环境下通常占用 2 个字节。 - 结果:输出 2。
- 类型提升陷阱
a = sizeof(3 + 5.0)
- 分析:
sizeof计算的是括号内表达式运算结果的数据类型长度。 - 过程:
3是整数 (int)。5.0默认是双精度浮点数 (double)。- 规则:C 语言运算时,"小类型"往"大类型"靠。
int+double\(\rightarrow\)double。 double类型占用 8 个字节。
- 结果:输出 8。
- 优先级陷阱
b = sizeof 3 + 5.0
- 分析:这里没有括号,拼的是优先级。
- 过程:
- 优先级:
sizeof运算符的优先级 高于+号。 - 第一步:先算
sizeof 3。因为 3 是int,所以sizeof 3等于 4。 - 第二步:表达式变成了
4 + 5.0。结果是9.0(double)。 - 第三步:赋值给
int b。浮点数9.0被强制截断为整数9。
- 优先级:
- 结果:输出 9。
- 字符串陷阱
sizeof("China")
- 分析:肉眼看到的字符数 vs 实际内存占用。
- 过程:
- 可见字符:C-h-i-n-a (5个)。
- 规则:字符串常量在内存结束处必须有一个隐形的结束标记
\0。 - 计算:5 + 1 = 6。
- 结果:输出 6。
答案:
2, 8, 9, 6
3、考察嵌套循环与 Continue
大概率是原题
#include<stdio.h>
int main()
{
int i, j, n = 0;
for(i = 0; i < 2; i++)
{
n++;
for(j = 0; j <= 3; j++)
{
if(j % 2) continue;
n++;
}
n++;
}
printf("n = %d\n", n);
return 0;
}
-
考点:逻辑流程控制,
continue跳过谁,n++执行了几次。 -
解析:
- 外层
i=0:n变成1。进入内层。 - 内层
j=0: 偶数不continue,n变成2。 - 内层
j=1: 奇数continue (跳过n++)。 - 内层
j=2: 偶数不continue,n变成3。 - 内层
j=3: 奇数continue。 - 内层结束。
n变成4 (外层最后还有个n++)。 - 外层
i=1:n变成5。重复一遍内层逻辑,n加3,变成8。
- 外层
-
答案:
n= 8
可以尝试掌握:(但大概率不考 就是考上面那题)
#include<stdio.h>
int main()
{
int n=5;
do
{
switch(n%2)
{
case 0:
n--;
break;
case 1:
n--;
continue;
}
n--;
printf("%2d",n);
}while(n>0);
return 0;
}
初始状态: n = 5
第一轮循环 (n=5)
- 进入
do。 switch(5 % 2)\(\rightarrow\) 结果是 1 \(\rightarrow\) 命中case 1。- Case 1 执行:
n--:n 变为 4。continue:【关键点】 直接跳到 while 判断处!- (后面的
n--和printf全部被跳过,没执行)
- While 判断:
4 > 0成立。继续循环。
第二轮循环 (n=4)
- 进入
do。 switch(4 % 2)\(\rightarrow\) 结果是 0 \(\rightarrow\) 命中case 0。- Case 0 执行:
n--:n 变为 3。break:跳出 switch,继续往下走。
- Switch 之后:
n--:n 变为 2。printf("%2d", 2):【输出】2。
- While 判断:
2 > 0成立。继续循环。
第三轮循环 (n=2)
- 进入
do。 switch(2 % 2)\(\rightarrow\) 结果是 0 \(\rightarrow\) 命中case 0。- Case 0 执行:
n--:n 变为 1。break:跳出 switch,继续往下走。
- Switch 之后:
n--:n 变为 0。printf("%2d", 0):【输出】0。
- While 判断:
0 > 0不成立。循环结束。
答案:
2 0
难点:
break在switch中:- 作用:仅仅跳出
switch语句块。 - 后果:程序继续向下执行
switch后面的代码(即n--和printf)。
- 作用:仅仅跳出
continue在switch中(且在循环里):- 作用:直接作用于外部的循环(do-while)。
- 后果:它不仅跳出 switch,还跳过了 switch 后面所有的代码(跳过了外面的
n--和printf),直接飞奔去进行while(n>0)的条件判断!
4、考察函数递归
大概率是原题
#include<stdio.h>
void fun(int a,int b)
{
if(a >= b)
printf("%d",a); // 【位置 A】
else
fun(a+1, b); // 【位置 B】递归调用
printf("%d",a); // 【位置 C】这是公用的代码!无论 if 还是 else,最后都会执行这一句!
}
int main()
{
fun(1,2);
return 0;
}
第一层:main 调用 fun(1, 2)
-
参数:
a=1,b=2。 -
判断:
if(1 >= 2)不成立。 -
执行 else:进入 【位置 B】,调用下一层递归
fun(2, 2)。- (注意:此时第一层函数暂停在这里,等待第二层运行结束,它手里还捏着最后的 【位置 C】 没执行)
第二层:递归调用
fun(2, 2)- 参数:
a=2,b=2。 - 判断:
if(2 >= 2)成立! - 执行 if:进入 【位置 A】,打印
2。- 当前屏幕:
2
- 当前屏幕:
- 继续执行:
if语句结束,程序继续向下走。 - 执行末尾:到达 【位置 C】,打印
2。- 当前屏幕:
22
- 当前屏幕:
- 结束:第二层函数运行完毕,返回上一层。
回到第一层:fun(1, 2)
- 继续:刚才在 【位置 B】 等待的递归调用结束了。
- 继续向下:程序走到最后的 【位置 C】。
- 此时的 a:在这一层,
a仍然是 1。 - 执行末尾:打印
1。- 当前屏幕:
221
- 当前屏幕:
答案:
221
练习:
#include<stdio.h>
void fun(int a)
{
if(!a)
printf("%d",a);
else
fun(a-1);
printf("%d",a);
}
int main()
{
fun(2);
return 0;
}
0012
5、考察共用体Union
大概率是原题
#include<stdio.h>
int main()
{
union { char c; int i; } s;
s.c = 'A'; // 内存里写入 65 (0x41)
s.i = 2; // 内存里写入 2。因为共用地址,'A' 被覆盖成了 2。
printf("%d%d", s.c, s.i);
return 0;
}
- 共用起始地址:
char c和int i并不是分开存放的,而是从同一个内存地址开始存放。- 可以把共用体想象成一个屋子,同一时间只能住一个人。
- 覆盖过程(Timeline):
s.c = 'A':内存的第 1 个字节被写入了 65('A'的ASCII码)。s.i = 2:这里是关键!int占用 4 个字节(包含第 1 个字节)。当你写入整数2时,系统会将这 4 个字节全部重写(通常是00000010 00000000...二进制)。- 结果:原本那个位置的 'A' (65) 瞬间被整数
2的低位数据覆盖掉了。
- 输出读取:
s.c:去读第 1 个字节 \(\rightarrow\) 发现是 2。s.i:去读整个 4 字节 \(\rightarrow\) 发现是 2。- 所以输出
2和2,即22。
答案:
22
6、考察宏定义
大概率是原题
#include<stdio.h>
#define LAB1 0
#define LAB2 2
#include<stdio.h>
void main()
{
int temp;
#ifdef LAB1
temp=LAB1;
#else
temp=LAB2;
#endif
printf("%d",temp);
}
- 区分
#ifdef和#if:#if LAB1:这是在判断 真假。如果代码写的是这个,因为LAB1是 0(假),确实会跳到else。#ifdef LAB1:这是在判断 存在性(Is Defined?)。它完全不关心LAB1的值是 0、1 还是 100,它只关心:“你在前面通过#define提到过LAB1这个名字吗?”
- 执行逻辑:
- 第一行
#define LAB1 0已经“注册”了LAB1这个名字。 - 所以在
#ifdef LAB1时,判定结果为 “存在”(真)。 - 编译器会保留
temp = LAB1;这一行代码。 - 即
temp = 0;。
- 第一行
- 总结:
- 只要
#define出现过,#ifdef就成立。哪怕你定义的是#define LAB1(后面什么都不写),它也是成立的。
- 只要
答案:
0
五、算法设计题(共2小题,每小题8分,共16分)
1、考察二维数组与循环
大概率是真题:
编程计算n×n的矩阵的两条对角线上所有元素(各元素数值任意)之和
#include <stdio.h>
#define N 5 // 【改这里】定义矩阵大小,如果是5就是5x5,如果是3就是3*3
int main()
{
int a[N][N] = {
{1, 1, 1, 1, 1},
{1, 1, 1, 1, 1},
{1, 1, 5, 1, 1},
{1, 1, 1, 1, 1},
{1, 1, 1, 1, 1}
};
// int a[N][N] = {
// {9, 4, 7},
// {5, 6, 1},
// {8, 3, 2}
// };
int sum = 0, i, j;
printf("%d x %d 数组:\n", N, N);
// 输出矩阵部分
for(i = 0; i < N; i++)
{
for(j = 0; j < N; j++)
printf("%3d", a[i][j]);
printf("\n");
}
// 计算求和部分
for(i = 0; i < N; i++)
{
for(j = 0; j < N; j++)
{
// 核心公式
// 1. i == j 是主对角线
// 2. i + j == N - 1 是副对角线 (3x3时是2, 5x5时是4)
if(i == j || i + j == N - 1)
{
sum = sum + a[i][j];
}
}
}
printf("对角线元素之和为:%d\n", sum);
return 0;
}
2、考察字符串与指针
大概率是真题
编程使用字符指针将26个英文字母以每行10个的形式输出
#include<stdio.h>
int main()
{
char *str1, *str2;
str1 = "abcdefghijklmnopqrstuvwxyz";
// 遍历字符
for(str2 = str1; str2 < str1 + 26; str2++)
{
printf("%c ", *str2);
// 判断是否换行
// str2-str1 得到的是当前字符的下标(0-25)
// +1 是为了变成第几个字符(1-26)
if((str2 - str1 + 1) % 10 == 0)
printf("\n");
}
return 0;
}
六、程序设计题(共2小题,每小题12分,共24分)
1、考察流程图(循环)
画出完成如下要求的程序对应的流程图:求两个自然数的最大公约数和最小公倍数
/* 【例 5.17】求两个自然数的最大公约数和最小公倍数 */
#include <stdio.h>
int main()
{
int a, b, r, n, m;
printf("请输入两个整数:");
scanf("%d%d", &a, &b);
m = a; n = b; /* 保存原始数值,因为后面 a 和 b 会变 */
/* 核心算法:辗转相除法 */
do
{
r = a % b; /* 求余数 */
a = b; /* 把除数 b 变成下一轮的被除数 a */
b = r; /* 把余数 r 变成下一轮的除数 b */
} while (r != 0); /* 当余数不为0时,继续循环;余数为0,结束 */
printf("%d和%d的最大公约数是:%d\n", m, n, a);
printf("最小公倍数是:%d\n", m * n / a);
return 0;
}
- 开始框(圆角矩形/椭圆):写上
开始。 - 输入框(平行四边形):写
输入 a, b。 - 处理框(矩形):写
m = a, n = b(保存原值)。 - 处理框(矩形):写
r = a % b。 - 处理框(矩形):写
a = b。 - 处理框(矩形):写
b = r。 - 判断框(菱形):写
r != 0 ?(或者r == 0?)。- 如果 是 (True):箭头指回到第 4 步(求余数的那一步)。
- 如果 否 (False):箭头向下继续。
- 处理框(矩形):计算最小公倍数
lcm = m * n / a(此时 a 已经是最大公约数了)。 - 输出框(平行四边形):写
输出 a, lcm。 - 结束框(圆角矩形/椭圆):写
结束。
2、可能还是考察画图(分支)
没有明确的暗示
自己掌握好相关知识点
怎么画图
题目一:寻找最大的数
题目二:有3个数 a, b, c,要求按大小顺序把它们输出
题目三:判断一个数 n 能否同时被 3 和 5 整除
3、也可能考察程序 不过还是画图可能性大些
出容易点就是
问题:编程打印数字金字塔。
0
123
23456
3456789
#include <stdio.h>
int main() {
int height = 4; // 金字塔高度
int i, j;
for (i = 0; i < height; i++) {
// 打印每行前的空格,使数字居中
for (j = 0; j < height - i - 1; j++) {
printf(" ");
}
// 打印数字,每行从 i 开始,长度为 2*i + 1
for (j = 0; j < 2 * i + 1; j++) {
printf("%d", i + j);
}
// 换行
printf("\n");
}
return 0;
}
问题:求阶乘。
#include <stdio.h>
int main() {
int n;
unsigned long long fact = 1;
scanf("%d", &n);
if (n < 0) {
printf("阶乘只适用于非负整数。\n");
return 1;
}
for (int i = 1; i <= n; i++) {
fact *= i;
}
printf("%d 的阶乘是 %llu\n", n, fact);
return 0;
}
💬 评论